package ua.kharkov.khpi.tarasenko.bach.core.logging;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.Transaction;

import ua.kharkov.khpi.tarasenko.bach.core.db.HibernateUtil;
import ua.kharkov.khpi.tarasenko.bach.dao.core.usersession.UserSessionDAO;
import ua.kharkov.khpi.tarasenko.bach.dao.mysql.usersession.UserSessionDAOImpl;

public class LogRollOut {

	private static final Logger LOG = Logger.getLogger(LogRollOut.class);

	private static final int SESSION_TIMEOUT = 60;

	private static final String USER_ITEM_PATTERN_STRING = "(\\d{2}):(\\d{2}):(\\d{2}).*uid=(\\d)*.*iid=(\\d)*.*";
	private static final String USER_ORDER_PATTERN_STRING = "(\\d{2}):(\\d{2}):(\\d{2}).*uid=(\\d)*.*oid=(\\d)*.*";

	private static final Pattern USER_ITEM_PATTERN = Pattern
			.compile(USER_ITEM_PATTERN_STRING);
	private static final Pattern USER_ORDER_PATTERN = Pattern
			.compile(USER_ORDER_PATTERN_STRING);

	// TODO split method on smaller ones
	public static void doRollOut(File log) throws IOException, ParseException {
		BufferedReader reader = new BufferedReader(new FileReader(log));
		String row = "";
		List<CustomerSession> sessions = new ArrayList<CustomerSession>();
		Map<Integer, SessionDatePair> currentSessions = new HashMap<Integer, SessionDatePair>();

		// Parse date from file name
		DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
		Date logFileDate = dateFormat.parse(log.getName().split(" ")[1]);

		while ((row = reader.readLine()) != null) {
			LOG.debug(row);

			Matcher matcher = null;
			TransactionType tType = null;
			boolean findResult = false;
			if (row.contains("order")) {
				matcher = USER_ORDER_PATTERN.matcher(row);
				tType = TransactionType.BUY;
			} else if (row.contains("viewed")) {
				matcher = USER_ITEM_PATTERN.matcher(row);
				tType = TransactionType.VIEW;
			} else if (row.contains("add")) {
				matcher = USER_ITEM_PATTERN.matcher(row);
				tType = TransactionType.CART;
			} else {
				LOG.error("ERROR! " + row);
			}
			findResult = matcher.find();
			if (findResult) {
				String dateString = matcher.group(1) + ":" + matcher.group(2)
						+ ":" + matcher.group(3);
				DateFormat formatter = new SimpleDateFormat("kk:mm:ss");
				Date transactionDate = formatter.parse(dateString);

				int uid = Integer.parseInt(matcher.group(4));
				int itemOrOrderId = Integer.parseInt(matcher.group(5));

				SessionDatePair sessionInTime = currentSessions
						.get(new Integer(uid));

				if (sessionInTime == null) {

					// If there is no session of this user - create new.
					CustomerSession customerSession = new CustomerSession();
					customerSession.setUserId(uid);

					Date sessionStartDate = getSessionStartDate(
							transactionDate, logFileDate);

					customerSession.setSessionStartDate(sessionStartDate);
					TransTypePair transaction = new TransTypePair(new Integer(
							itemOrOrderId), tType);
					customerSession.addTransaction(transaction);
					sessionInTime = new SessionDatePair(customerSession,
							transactionDate);
					currentSessions.put(uid, sessionInTime);
				} else {

					// There is session of this user, check if it is valid or
					// expired
					Date lastTransDate = sessionInTime
							.getSessionLastAccessTime();
					long timeDiff = transactionDate.getTime()
							- lastTransDate.getTime();
					int minutes = (int) (timeDiff / (1000 * 60));

					if (minutes >= SESSION_TIMEOUT) {

						// Session expired, put old into session list and create
						// new in map
						sessions.add(sessionInTime.getCustomerSession());
						CustomerSession customerSession = new CustomerSession();
						customerSession.setUserId(uid);
						customerSession
								.setSessionStartDate(getSessionStartDate(
										transactionDate, logFileDate));
						TransTypePair transaction = new TransTypePair(
								new Integer(itemOrOrderId), tType);
						customerSession.addTransaction(transaction);
						sessionInTime = new SessionDatePair(customerSession,
								transactionDate);
						currentSessions.put(uid, new SessionDatePair(
								customerSession, transactionDate));
					} else {

						sessionInTime.getCustomerSession().addTransaction(
								new TransTypePair(new Integer(itemOrOrderId),
										tType));
						sessionInTime.setSessionLastAccessTime(transactionDate);
					}

				}

			} else {
				LOG.error("Unparseble row in log file!");
			}

		}
		reader.close();

		// put all sessions in currentSessions into sessions
		for (Map.Entry<Integer, SessionDatePair> entry : currentSessions
				.entrySet()) {
			sessions.add(entry.getValue().getCustomerSession());
		}
		
		saveToDB(sessions);
		
		for (CustomerSession session : sessions) {
			LOG.debug(session);
		}
	}

	private static Date getSessionStartDate(Date transactionDate,
			Date logFileDate) {
		Calendar calTime = Calendar.getInstance();
		Calendar calDate = Calendar.getInstance();

		calTime.setTime(transactionDate);
		calDate.setTime(logFileDate);

		calDate.set(Calendar.HOUR_OF_DAY, calTime.get(Calendar.HOUR_OF_DAY));
		calDate.set(Calendar.MINUTE, calTime.get(Calendar.MINUTE));
		calDate.set(Calendar.SECOND, calTime.get(Calendar.SECOND));
		calDate.set(Calendar.MILLISECOND, calTime.get(Calendar.MILLISECOND));

		return calDate.getTime();
	}
	
	private static void saveToDB(List<CustomerSession> sessions) {
		Session hibernateSession = HibernateUtil.getSession();
		UserSessionDAO userSessionDAO = new UserSessionDAOImpl(hibernateSession);
		Transaction transaction = hibernateSession.beginTransaction();
		for (CustomerSession session : sessions) {
			userSessionDAO.save(session);
		}
		transaction.commit();
		hibernateSession.close();
	}
}
